// Copyright  2004-2008 Backplane Ltd.
//
// This file is part of Ubiquity formsPlayer
//
// Ubiquity formsPlayer is free software; you can redistribute it and/or modify it
// under the terms of the GNU General Public License as published by the
// Free Software Foundation; either version 3 of the License, or (at your
// option) any later version.
//
// Ubiquity formsPlayer is distributed in the hope that it will be useful, but WITHOUT
// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
// more details.
//
// You should have received a copy of the GNU General Public License
// along with Ubiquity formsPlayer If not, see <http://www.gnu.org/licenses/>.
//	
//
//   Purpose: implementation of CVertex.
//   $Id$



// Vertex.cpp: implementation of the CVertex class.
//
//////////////////////////////////////////////////////////////////////
#include "stdafx.h"
#include "Vertex.h"
#include "model.h"
#include "bindingTarget.h"
//I'm only including this to be able to set the 
//	id resolver, if it gets set by passing an argument instead,
//	this can be removed
#include "XformsXformsElement.h"
#include "XForms.h"
#include "boundnode.h"
#include "XMLState.h"

#include <atlrx.h>
//////////////////////////////////////////////////////////////////////
// Construction/Destruction
//////////////////////////////////////////////////////////////////////

CVertex::CVertex
(
	CBoundNode * pInstanceNode,
	CVertex::EType eType,
	CIdResolver * pResolver,
	CImplModel * pModel
):
	m_pInstanceNode(pInstanceNode)
	,m_type(eType)
	,m_bVisited(false)
	,m_pIndex(NULL)
	,m_sComputedExpression(0)
	,m_lInDegree(0)
	,m_pIdResolver(pResolver)
	,m_pModel(pModel)
{

}

CVertex::~CVertex()
{
	if(m_pInstanceNode)
	{
		// Remove this from the constraints list
		m_pInstanceNode->RemoveConstraint(this);
		if(m_type != Constraint)
			m_pInstanceNode->AddPropertyVertex(0,m_type);
		m_pInstanceNode->Release();
	}
}

void CVertex::addDependent(CVertex * pVertex)
{
	if (this != pVertex)
		m_lDepList.AddHead(pVertex);
}

/////////////////////////////////////////////////////////////////////////
//
//								CVertexList
//
/////////////////////////////////////////////////////////////////////////

CVertex * CVertexList::createVertex(CBoundNode * pInstance, CVertex::EType eType, CIdResolver * pResolver,CImplModel * pModel)
{
	//I don't think this function is really needed, but I'm not sure.
	return new CVertex(pInstance,eType, pResolver,pModel);
}

CVertex * CVertexList::findVertex(IXMLDOMNode * pInstanceNode,CVertex::EType eType)
{
/*	CVertex * pRet = NULL;
	IUnknown * pUnk1 = NULL;
	pInstanceNode->QueryInterface(_uuidof(IUnknown),(void**)&pUnk1);
	POSITION pos = m_lNodelist.GetHeadPosition();
	
	while(pos)
	{
		IUnknown * pUnk2 = NULL;
		pRet = m_lNodelist.GetNext(pos);
		pRet->m_pInstanceNode->QueryInterface(_uuidof(IUnknown),(void**)&pUnk2);
		if (
			(pUnk1 == pUnk2)
			&& (pRet->m_type == eType))
		{
			break;
		}
		::safeRelease(pUnk2);
		pRet = NULL;
	}
	
	::safeRelease(pUnk1);
	return pRet;
*/
	return NULL;
}

HRESULT CVertexList::addVertex
	(
		CImplModel * pModel,
		IXMLDOMNode * pContext,
		CComBSTR sRef,
		bool bApplyFirstNodeRule,
		CVertex::EType eType,
		CVertex * vDependent,
		CTypedPtrArray<CPtrArray,CVertex*> * pListToFill,
		CIdResolver * pResolver,
		bool bCreateIfNotFound
	)
{
	HRESULT hr;

	{
		pModel->m_pXForms->putXPathIdentityResolver(pResolver);
		if(bApplyFirstNodeRule)
		{
			//fetch the node;
			CComPtr<IXMLDOMNode> pInstanceNode;
			hr = pModel->getInstanceNode(sRef,pContext,bCreateIfNotFound,&pInstanceNode);
			// Bug 238 - getInstanceNode can in some cases return a null pointer and still 
			// return a successful result so both the hr & the pInstance should be check. 
			// Otherwise a vertex is created for a Node that doesn't exist.
			if(SUCCEEDED(hr) && pInstanceNode)
			{
				//set the vertex
				CVertex * v =0;
				hr = setVertex(pInstanceNode, eType, vDependent, pResolver,pModel,&v);
				if(FAILED(hr))
				{
					CComPtr<IErrorInfo> pErr;
					::GetErrorInfo(0,&pErr);
					CComBSTR sDesc;
					if(pErr)
						pErr->GetDescription(&sDesc);
					pModel->onBindingException(sRef,sDesc);
				}
				// add the vertex to the list of vertices
				//	that we have been asked to fill
				//	for a nodeset binding.
				if(pListToFill)
					pListToFill->Add(v);
			}
		}
		else
		{
			CComPtr<IXPathResult> pResult;
			hr = pModel->getInstanceNodeList(sRef, pContext,bCreateIfNotFound,&pResult);
			if(SUCCEEDED(hr) && pResult)
			{
				unsigned long len;
				pResult->get_snapshotLength(&len);
			
				for(unsigned long i = 0;i<len;++i)
				{
					CComPtr<IXMLDOMNode> pInstanceNode;
					pResult->snapshotItem(i,&pInstanceNode);

					//set the vertex
					CVertex * v = 0;
					hr = setVertex(pInstanceNode, eType, vDependent,pResolver,pModel,&v);
					if(FAILED(hr))
					{
						CComPtr<IErrorInfo> pErr;
						::GetErrorInfo(0,&pErr);
						CComBSTR sDesc;
						if(pErr)
							pErr->GetDescription(&sDesc);
						pModel->onBindingException(sRef,sDesc);
					}
					//model now set in constructor for vertex
					//v->m_pModel = pModel;
					// add the vertex to the list of vertices that we have been asked to fill
					//	for a nodeset binding.
					if(pListToFill)
						pListToFill->Add(v);
				}
			}
		}
		pModel->m_pXForms->putXPathIdentityResolver(0);
	}
	return hr;
	

/*JSCRIPT
	 // See if our references are embedded in a function call
	 

	var sSearch = "\\w*\\((.*)\\)";
	var sFlags = "";
	var re = new RegExp(sSearch, sFlags);
	var a = sRef.match(re);

	if (a)
		sRef = a[1];

	
	// Now deal with ordinary references
	 

	var sSearch = "(/?(?:(?:[\\w:]*)/?)*(?:@[\\w:]*)?)";
	var sFlags = "g";
	var re = new RegExp(sSearch, sFlags);
	var a = sRef.match(re);

	if (a && (a.length > 2)) {
		for (var i = 0; i < a.length; i++)
			if ((a[i] != "") && (a[i].substring(0, 1) != "'"))
				this.setVertex(oModel, a[i], Instance, vDependent);
		vRet = null;
	}
	else
		vRet = this.setVertex(oModel, a[0], eType, vDependent);
	return vRet;

*/


}

//public setVertex
void CVertexList::setVertex
(
	BSTR sXPath,
	IXMLDOMNode * pNode,
	CVertex::EType eType,
	CVertex * pDependentVertex,
	CIdResolver * pResolver,
	CImplModel * pModel
)
{
	//create the vertex between the target node and the property
	CVertex * pv = 0;
	HRESULT hr = setVertex(pNode,eType,pDependentVertex,pResolver,pModel,&pv);
	if(FAILED(hr))
	{
		CComPtr<IErrorInfo> pErr;
		::GetErrorInfo(0,&pErr);
		CComBSTR sDesc;
		if(pErr)
			pErr->GetDescription(&sDesc);
		pModel->onBindingException(sXPath,sDesc);
	}

	pv->m_sComputedExpression = sXPath;
	//create a vertex and related vertices between the 
	//	value of the property and whatever it depends on.
	// The original target node is used as evaluation context
	//	for the XPath in the property.
	addVertex(pModel,pNode, sXPath, true, CVertex::EType::Instance, pv,NULL,pResolver,false);	
}

//private setVertex
	HRESULT CVertexList::setVertex
	(
		IXMLDOMNode *pInstanceNode,
		CVertex::EType eType,
		CVertex * vDependent
		,CIdResolver * pResolver
		,CImplModel * pModel
		,CVertex ** ppvRetVal
	)
	{
		HRESULT hr = S_OK;
		CBoundNode * pBoundNode = NULL;
		if (pInstanceNode)
		{
			//Get the CBoundNode object corresponding to that node
			pBoundNode = CBoundNode::GetBoundNode(pInstanceNode,pModel->m_pSchemaCache);
			// Bug 249 - BoundNode's need to know about the XFormsXFormsElement so that 
			// it can check whether the process is shutdown so that the targets are not accessed
			// as they can occasionally already have been deleted.
			// NOTE :: BoundNode's shouldn't know about the existents of the XFormsXFormsElement
			// so that it can be easliy move to a seperate module.
			if(pBoundNode)
				pBoundNode->putXForms(pModel->m_pXForms);
		}	

		CVertex * v = 0;
		bool bNodeExistsAlready = false;
		if	(pBoundNode && pBoundNode->m_pValueVertex)
		{
			bNodeExistsAlready = true;
		}
		if(bNodeExistsAlready && eType == CVertex::EType::Instance)
		{
			//If this is binding to the instance, 
			//	and pBoundNode already has an instanceVertex,
			//	return that.  
			//	This means that the bound node in question
			//	was not created by CBoundNode::GetBoundNode
			//	but by a previous call.
			v = pBoundNode->m_pValueVertex;
			pBoundNode->Release();
		}
		else
		{

			//This vertex should be destroyed in the destructor of VertexList.
			v = new CVertex(pBoundNode, eType, pResolver,pModel);	 

			//Add the new vertex to the list of vertices so that it can be fetched
			//	and/or destroyed later.
			m_lVertices.AddTail(v);

			// add our dependent to the list of dependents
			if (vDependent)
				v->addDependent(vDependent);
			
			//Attach the vertex to CBoundNode if one exists.

			if(pBoundNode)
			{
				hr = pBoundNode->AddPropertyVertex(v,eType);
				//Check the return value, adding a vertex to a unique property 
				//	that already has a vertex attached is an error.
				if(SUCCEEDED(hr))
				{
					
					//Also add the parent, to allow for inheritance of properties.
					IXMLDOMNode * pParent = NULL;
					IXMLDOMNode * pNode = 0;
					hr = pBoundNode->getNode(&pNode);
					if(SUCCEEDED(hr))
					{
						pNode->get_parentNode(&pParent);
						if(pParent)
						{
							CVertex * pvParent = 0;
							hr = setVertex(pParent,CVertex::EType::Instance,NULL, pResolver,pModel,&pvParent);
							//Moved to the GetBoundNode, associating children with parents should be handled
							//	by the extended DOM network associated with CBoundNode, and nothing to do with 
							//	CVertex.
							//pvParent->m_pInstanceNode->addBoundChild(pBoundNode);
						}
						::safeRelease(pParent);
					}
					::safeRelease(pNode);
				}
			}
		}
	
		*ppvRetVal = v;
		return hr;
	}


HRESULT CVertex::evaluate()
{
	HRESULT hr = S_OK;
	if(m_sComputedExpression.Length() > 0)
	{
		//Set the id resolver for XPath expressions.
		hr = m_pModel->m_pXForms->putXPathIdentityResolver(m_pIdResolver);
		if(SUCCEEDED(hr))
		{
			IXMLDOMNode * pContext= 0;
			m_pInstanceNode->getNode(&pContext);
			if(m_type == Calculate)
			{
				CComBSTR sValue;
				m_pModel->getInstanceString(m_sComputedExpression,pContext,&sValue);
				m_pInstanceNode->setValue(sValue,true);
			}
			else
			{
				bool bVal = m_pModel->getInstanceBoolean(pContext,m_sComputedExpression);
				switch(m_type)
				{
					case Readonly:
						m_pInstanceNode->setReadOnly(bVal);
						break;
					case Relevant:
						m_pInstanceNode->setRelevant(bVal);
						break;
					case Required:
						m_pInstanceNode->setRequired(bVal);
						break;
					case Constraint:
						hr =  m_pInstanceNode->setConstraint(this,bVal);
						break;
				}
			}
			::safeRelease(pContext);
			//reset the id resolver for XPath expressions to NULL.
			m_pModel->m_pXForms->putXPathIdentityResolver(0);
		}
	}
	return hr;
}

bool CVertexList::OnDeleteRequest()
{
	FlushVertexList();
	return true;
}

void CVertexList::FlushVertexList()
{
	while(!m_lVertices.IsEmpty())
	{
#ifndef CATCH_VXLIST_DELETION_ERRORS_AT_SOURCE 
		try
		{
#endif
			delete m_lVertices.RemoveHead();
#ifndef CATCH_VXLIST_DELETION_ERRORS_AT_SOURCE 
		}
		catch(...){ASSERT(0);}
#endif
	}
}



void CVertexList::evaluate()
{
	//Temp, this runs through and reevaluates all vertices.
	EnterDeletionPostponementState();
	try
	{
		POSITION pos  = m_lVertices.GetHeadPosition();
		while(!IsAwaitingDeletion() && pos)
		{
			try {
				m_lVertices.GetNext(pos)->evaluate();
			}
			catch(...) {
				AtlTrace("\n!formsPlayer.dll - CVertexList::evaluate\n");
			}
		}
	}
	catch(...)
	{
		ASSERT(0);
	}

	ExitDeletionPostponementState();
	
}
#define CATCH_VXLIST_DELETION_ERRORS_AT_SOURCE
#undef CATCH_VXLIST_DELETION_ERRORS_AT_SOURCE
CVertexList::~CVertexList()
{
	FlushVertexList();
/*	POSITION pos  = m_Nodemap.GetStartPosition();
	IUnknown * pUnk = 0;
	CBoundNode * pToDelete = 0;
	while(pos)
	{
		m_Nodemap.GetNextAssoc(pos,(void*&)pUnk,(void*&)pToDelete);
		m_Nodemap.RemoveKey(pUnk);
		delete pToDelete;
		
		// Do not Release pUnk, pUnk is just a number,
		//		It was released when it was inserted into the map.
		//::safeRelease(pUnk);
	}
*/
}

HRESULT CVertexList::GetXMLState(IXmlState ** ppState)
{
	//Create an instance of the state object
	CComObject<CXmlState> * pState = NULL;
	CComObject<CXmlState>::CreateInstance(&pState);
	
	//supply it with the necessary information
//	pState->initialise(&m_Nodemap);
	
	return pState->QueryInterface(IID_IXmlState,(void**)ppState);

}

// Called to add this vertex to the pertinent dependency subgraph of the appropriate model.
HRESULT CVertex::makePertinent(void)
{
	HRESULT hr = E_FAIL;
	if(m_pModel)
	{
		hr = m_pModel->makePertinent(this);
	}
	return hr;
}

/*
CBoundNode * CVertexList::GetBoundNode(IXMLDOMNode *pXMLNode,IXMLDOMSchemaCollection * pSchemaCache)
{
	//Looks up the bound node in the member map, if none exists, 
	//	creates it and adds it to the map.

	if(pXMLNode)
	{
		//Get the IUnknown pointer, we will use this to compare nodes.
		IUnknown * pUnk1 = NULL;
		pXMLNode->QueryInterface(_uuidof(IUnknown),(void**)&pUnk1);

		CBoundNode * pBoundNode = 0;
		
		if(!m_Nodemap.Lookup((void*)pUnk1,(void*&)pBoundNode))
		{
			// lookup has failed.  create a new one.
			//This will be destroyed in the destructor of CVertexList
			pBoundNode = new CBoundNode(pXMLNode,pSchemaCache);
			//Add it to the map to allow us to look it up and/or delete it later
			m_Nodemap.SetAt(pUnk1,pBoundNode);
		}
		//clean up pUnk and return the pointer.
		::safeRelease(pUnk1);
		return pBoundNode;
	}
	else
		return NULL;
}
*/
HRESULT CVertexList::FindDependees(BSTR sXPathExpression , CVertex * pvxDependent)
{
	HRESULT hr = S_OK;
	CAtlRegExp<CAtlRECharTraitsW> reExpr;
	// five match groups: scheme, authority, path, query, fragment
	REParseError status = reExpr.Parse(L"(/?(?:(?:[\\w:]*)/?)*(?:@[\\w:]*)?)");//L"\\w*\\((.*)\\)");
	if (REPARSE_ERROR_OK != status)
	{
		// Unexpected error.
		hr = E_FAIL;
	}
	else
	{

		CAtlREMatchContext<CAtlRECharTraitsW> mcSubExpressions;
		if (!reExpr.Match(sXPathExpression,&mcSubExpressions))
		{
			// Unexpected error.
			hr = E_FAIL;
		}
		else
		{
			for (UINT nGroupIndex = 0; nGroupIndex < mcSubExpressions.m_uNumGroups;
				++nGroupIndex)
			{
				const CAtlREMatchContext<CAtlRECharTraitsW>::RECHAR* szStart = 0;
				const CAtlREMatchContext<CAtlRECharTraitsW>::RECHAR* szEnd = 0;
				mcSubExpressions.GetMatch(nGroupIndex, &szStart, &szEnd);

				ptrdiff_t nLength = szEnd - szStart;
				
				ATLTRACE("%d: \"%.*s\"\n", nGroupIndex, nLength, szStart);
			}
		}
	}
	return hr;
}
/*



	CAtlRegExp<CAtlRECharTraitsW> reExtractDependees;
	CAtlRegExp<CAtlRECharTraitsW>::RECHAR* szParse = L"\\w*\\((.*)\\)";
	
	REParseError status = reExtractDependees.Parse(szParse);

	if(REPARSE_ERROR_OK == status)
	{
		CAtlREMatchContext<CAtlRECharTraitsW> mc;
		CStringW psExpr = sRef;

		CStringW sNewExpr;
		const CAtlREMatchContext<CAtlRECharTraitsW>::RECHAR* szNext = psExpr;

		// Step through the matched chunks.
		while (reExtractDependees.Match(szNext, &mc))
		{
//			int i = 0;
//			mc.m_uNumGroups
			//Get the beginning and end of the desired chunk.
			const CAtlREMatchContext<CAtlRECharTraitsW>::RECHAR* szStart = 0;
			const CAtlREMatchContext<CAtlRECharTraitsW>::RECHAR* szEnd = 0;
			mc.GetMatch(0, &szStart, &szEnd);
			
			//If we do have a beginning and end, extract the chunk
			if (szStart && szEnd)
			{
				ptrdiff_t nLeadIn = szStart - szNext;
				szNext += nLeadIn;
				CStringW wsSubExpr = mc.m_Match.szStart;
				int iLen = mc.m_Match.szEnd - mc.m_Match.szStart;
				wsSubExpr = wsSubExpr.Left(iLen);
				//We now have a subexpression, we
				//	can use that to fetch any 
				//	nodes that this node is a dependent of.
				
				setVertex
				(
					wsSubExpr.AllocSysString(),
					pContext,
					CVertex::EType::Instance,
					NULL,//v,
					pResolver,
					pModel
				);
			}
		}
	}
*/
/*
class CXPathExpression
{
	CXPathChunk m_FirstSubExpression;
}
class CXPathChunk
{
	CXPathChunk * m_pFirstSubExpression;
	relationship;
} 
class CXPathLiteral
	:public CXPathChunk
{
	value;
	type;
}

class CXPathFunction
	:public CXPathChunk
{
	name;
	CTypedPtrList<CPtrList, CXPathChunk> m_Parameters;
}

class CXPathStep
{
	name;
	CTypedPtrList<CPtrList,CXPathFilters> m_Filters
}

class CXPathNodeTest
	:public CXPathChunk
{
	CTypedPtrList<CPtrList, CXPathStep> m_steps;
}

class CXPathFilter
{
	CXPathExpression m_Expression;
}

parseXPath(char * s)
{
	char * pcIterator = s;
	while(*pcIterator)
	{
		switch(*pcIterator)
		{
		case '[':
		case '(':

		}
		++pcIterator;
	}
}
*/
// <summary>validates all the nodes pointed to in the list</summary>
HRESULT CVertexList::validate(void)
{	
	
	POSITION pos  = m_lVertices.GetHeadPosition();
	HRESULT hr = pos?S_OK:S_FALSE;
	while(pos)
	{
		CBoundNode * pBN = m_lVertices.GetNext(pos)->m_pInstanceNode;
		if(pBN)
			pBN->validate();
	}
	return hr;
}
